#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sqlite3.h> //for sqlite3_open ..
#include <signal.h>
#include <time.h>//--------
#define PATH_DICT "./dict.txt" //单词表
#define PATH_DATA "./mydict.db" //数据库
#define N 32
#define M 128
#define R 0x1 //注册
#define L 0x2 //登录
#define Q 0x3  //查询单词
#define H 0x4  //历史记录
#define E 0x5 //退出

typedef struct{
	int type;//消息类型
	char name[N];//用户名
	char text[M];//单词  或 密码
}MSG;
#define LEN_SMG sizeof(MSG)
#define err_log(log)\
	do{\
	 perror(log);\
	 exit(1);\
	}while(0)

typedef struct sockaddr SA; 

void process_register(int clientfd,MSG *msg,sqlite3 *db);
void process_login(int clientfd,MSG *msg,sqlite3 *db);
void process_query(int clientfd,MSG *msg,sqlite3 *db);
void process_history(int clientfd,MSG *msg,sqlite3 *db);
void insert_history(MSG *msg,sqlite3 *db);
void get_time(const char *date);
void handler(int arg)
{
	wait(NULL);
}
int main(int argc, const char *argv[])
{
	int serverfd,clientfd;
	struct sockaddr_in serveraddr,clientaddr;
	socklen_t len=sizeof(SA);
	int cmd;  //登录选择
	char clean[M]={0};
	MSG msg;  //数据结构体
	pid_t pid;
	sqlite3 *db;  //数据库句柄
	ssize_t bytes;
	if(argc!=3)
	{
		sprintf(stderr,"User:%s <IP> <port>",argv[0]);
		return -1;
	}

	if(sqlite3_open(PATH_DATA,&db)!=SQLITE_OK) //获取句柄
	{
			printf("%s\n",sqlite3_errmsg(db));
			return -1;
	}
	if((serverfd=socket(AF_INET,SOCK_STREAM,0))<0)  //创建套接字
	{
		err_log("fail to socket");
	}
	serveraddr.sin_family=AF_INET;   //IPv4协议
	serveraddr.sin_port=htons(atoi(argv[2]));  //端口号
	serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
	if(bind(serverfd,(SA*)&serveraddr,len)<0) //绑定套接字
	{
		err_log("fail to bind");
	}
	if(listen(serverfd,10)<0)  //监听套接字 
	{
		err_log("fail to listen");
	}
 	signal(SIGCHLD,handler);//处理僵尸进程

	while(1)
	{
		if((clientfd=accept(serverfd,(SA*)&clientaddr,&len))<0) //链接客户端请求，产生新的套接字进行通信
		{
			perror("fail to accept");
			continue;
		}
		pid=fork();  //创建进程
		if(pid<0)
		{
			perror("fail to fork");
			continue;
		}
		else if(pid==0)  //子进程
		{
			close(serverfd);
			while(1)
			{
				bytes=recv(clientfd,&msg,LEN_SMG,0);  //接收客户端的数据
				if(bytes<=0)
					break;
				switch(msg.type) //根据数据类型进行选择
				{
					case R:
						process_register(clientfd,&msg,db); //注册函数
						break;
					case L:
						process_login(clientfd,&msg,db); //登录函数
						break;
					case Q:
						process_query(clientfd,&msg,db); //查询单词函数
						break;
					case H:
						process_history(clientfd,&msg,db); //查看历史函数
						break;
					case E:                
					       exit(0);   //退出
				}
			}
			close(clientfd);
			exit(1);
		}
		else
		{
			close(clientfd);
		}


	}
	
	return 0;
}
void process_register(int clientfd,MSG *msg,sqlite3 *db) //注册函数
{
	char sql[M]={0};
	char *errmsg;

	sprintf(sql,"insert into user values('%s','%s')",msg->name,msg->text);
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		printf("%s\n",errmsg);

		strcpy(msg->text,"Fail"); //将fail传递给msg->text
		send(clientfd,msg,LEN_SMG,0);
		return;
	}
	else
	{
		strcpy(msg->text,"OK");
		send(clientfd,msg,LEN_SMG,0);
		return;
	}

}
void process_login(int clientfd,MSG *msg,sqlite3 *db) //登录函数  
{
	char sql[M]={0};  
	char *errmsg;   //出错参数
	char **rep;    //查询指针
	int n_row;    //行
	int n_column;//列

	sprintf(sql,"select * from user where name='%s' and pwd='%s'",msg->name,msg->text);
	//使用用户名和密码查找
	if(sqlite3_get_table(db,sql,&rep,&n_row,&n_column,&errmsg)!=SQLITE_OK)
	{
		printf("%s\n",errmsg);
		strcpy(msg->text,"Fail");
		send(clientfd,msg,LEN_SMG,0);
		return;
	}
	else
	{
		if(n_row==0)//查不到
		{

			strcpy(msg->text,"Fail");
			send(clientfd,msg,LEN_SMG,0);
			return;
		}
		else //查到用户名及密码
		{

			strcpy(msg->text,"OK");
			send(clientfd,msg,LEN_SMG,0);
			return;
		}
	}
}
void process_query(int clientfd,MSG *msg,sqlite3 *db) //查询单词函数
{
	FILE *dp;
	char buf[1024]={0};
	MSG temp;
	int ret;
	char *p;
	memcpy(&temp,msg,LEN_SMG);//保存原来的信息
	if((dp=fopen(PATH_DICT,"r"))==NULL) 
	{
		perror("fail to fopen");
		strcpy(msg->text,"Fail");
		send(clientfd,msg,LEN_SMG,0);
		return;
	}
	strcpy(msg->text,"OK");
	send(clientfd,msg,LEN_SMG,0);

	while(fgets(buf,sizeof(buf),dp)!=NULL)
	{
	    	ret=strncmp(buf,temp.text,strlen(temp.text));//比较传过来的单词和BUF
			if(ret<0)//buf中的单词比   发来的单词小  继续比较
			{
				continue;
			}
			else if(ret>0)//buf中的单词  比发来的大  退出（找不到）
			{
				break;
			}
			else if(ret==0 && buf[strlen(temp.text)]!=' ')//与BUF中的几个相同 但是后一个不是空格 退出
			{
				break;
			}
			p=buf+strlen(temp.text);//把指针定位到第一个空格位置
			while(*p==' ')//定位到单词的意思
				p++;
			strcpy(msg->text,p);
			send(clientfd,msg,LEN_SMG,0);
			insert_history(&temp,db);
			fclose(dp);
			return;
	}
	strcpy(msg->text,"no found");
	send(clientfd,msg,LEN_SMG,0);
	fclose(dp);
	return;

}
void insert_history(MSG *msg,sqlite3 *db)  //插入历史记录
{
		char sql[M]={0};
		char *errmsg;
		char date[64]={0};
		get_time(date);//获得当前的日期
		sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,msg->text,date); //拼接时间数据
		if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) 
		{
			printf("%s\n",errmsg);
			return;
		}
}	
void get_time(const char *date)//获得时间
{
	time_t mytime;
	struct tm *mytm;
	mytime=time(NULL);//得到秒数
	mytm=localtime(&mytime);//得到当前的时间
	sprintf(date,"%04d-%02d-%02d  %02d:%02d:%02d",mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,\
			mytm->tm_hour,mytm->tm_min,mytm->tm_sec);

}
void process_history(int clientfd,MSG *msg,sqlite3 *db)  //查看历史函数
{
		char sql[M]={0};
		char *errmsg;  
		char **rep; //查询结果指针
		int n_row;  //行
		int n_column; //列
		int i,j;
		sprintf(sql,"select * from record where name='%s'",msg->name);
		if(sqlite3_get_table(db,sql,&rep,&n_row,&n_column,&errmsg)!=SQLITE_OK)
		{
				printf("%s\n",errmsg);
				strcpy(msg->text,"Fail");  //出错
				send(clientfd,msg,LEN_SMG,0);  //发送到客户端
				return;
		}
		else
		{
			if(n_row==0)  //行
			{
				
				strcpy(msg->text,"Fail");
				send(clientfd,msg,LEN_SMG,0);
				return;
			}
			else
			{
				
				strcpy(msg->text,"OK");
				send(clientfd,msg,LEN_SMG,0);

				for(i=0;i<n_row+1;i++)
				{
					for(j=0;j<n_column;j++)	
					{
						strcpy(msg->text,*rep++); //指针移动取值并拷贝
						send(clientfd,msg,LEN_SMG,0);
						usleep(1000);//防止粘包
					}
				}
				strcpy(msg->text,"over");
				send(clientfd,msg,LEN_SMG,0);
				return;
			}
		}
}
